/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.support;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import fr.x9c.cadmium.kernel.Block;
import fr.x9c.cadmium.kernel.ByteCodeParameters;
import fr.x9c.cadmium.kernel.CadmiumException;
import fr.x9c.cadmium.kernel.Context;
import fr.x9c.cadmium.kernel.Fatal;
import fr.x9c.cadmium.kernel.Interpreter;
import fr.x9c.cadmium.kernel.Value;
import fr.x9c.cadmium.primitives.cadmium.Cadmium;
import fr.x9c.cadmium.primitives.graph.GraphSlot;
import fr.x9c.cadmium.util.RandomAccessInputStream;

/**
 * This class implements an applet based upon the OCaml <i>Graphics</i>
 * library. The applet UI is reduced to a single canvas whose content is
 * controlled by the <i>Graphics</i> library. <br/>
 * <br/>
 * Bytecode is loaded from the resource <tt>/resources/cadmium/applet</tt>
 * and run by the constructor using parameters loaded from the resource
 * <tt>/resources/cadmium/parameters.properties</tt> (<i>arguments</i> array
 * is empty). <br/>
 * Such bytecode is intended to use either <i>Cadmium.register_applet</i>
 * or <i>CadmiumObj.register_applet</i> to register callbacks that will
 * be used for applet methods ({@link java.applet.Applet#init()},
 * {@link java.applet.Applet#start()}, {@link java.applet.Applet#stop()},
 * {@link java.applet.Applet#destroy()}). Additionally, a callback is
 * registered for a <i>run</i> function that is called by the thread this
 * applet launches right after <i>start</i> code. It is thus safe to wait
 * for <i>Graphics</i> events from this <i>run</i> function.
 * The thread executing the <i>run</i> function is killed when the applet is
 * destroyed. <br/>
 * All the callback methods are passed an unique argument that is the actual
 * {@link fr.x9c.cadmium.support.Applet} object. <br/>
 * <br/>
 * One should also notice that the jar file used as an applet archive should
 * be signed, as the Cadmium interpreter relies on reflection. <br/>
 * <br/>
 * This class is the main class of jar files created using the
 * <tt>cadmium.make-applet</tt> ant macro.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.1
 * @since 1.0
 */
public final class ByteCodeApplet extends Applet {

    /** Serialization UID. */
    static final long serialVersionUID = -4527531143267676400L;

    /** Path to 'applet parameters' resource. */
    private static final String PARAMETERS = "/resources/cadmium/parameters.properties";

    /** Path to 'applet bytecode' resource. */
    private static final String APPLET = "/resources/cadmium/applet";

    /** Interpreter of applet code. */
    private final Interpreter interpreter;

    /**
     * Executes the Cadmium interpreter. <br/>
     * Parameters as well as bytecode are loaded from jar resources.
     * @throws IOException if an error occurs while loading bytecode or
     *                     parameters
     * @throws Fatal.Exception if bytecode or parameters are missing
     * @throws Fail.Exception as thrown by the main code
     * @throws CadmiumException as thrown by the main code
     */
    public ByteCodeApplet()
        throws IOException, Fatal.Exception, CadmiumException {
        super();

        // resources
        final InputStream propsStream =
            ByteCodeApplet.class.getResourceAsStream(PARAMETERS);
        final InputStream bytecodeStream =
            ByteCodeApplet.class.getResourceAsStream(APPLET);
        if ((propsStream == null) || (bytecodeStream == null)) {
            Fatal.raise("bytecode/parameters ressource is missing");
        } // end if

        // interpreter parameters
        final Properties props = new Properties();
        props.load(propsStream);
        final ByteCodeParameters params =
            ByteCodeParameters.fromProperties(props,
                                              "applet",
                                              new String[0],
                                              System.in,
                                              System.out,
                                              System.err);
        propsStream.close();

        // interpreter execution
        final RandomAccessInputStream bytecode =
            new RandomAccessInputStream(bytecodeStream);

        this.interpreter =
            new Interpreter(new ByteCodeParameters(params.getArguments(),
                                                   false, /* backtrace */
                                                   false, /* stop JVM */
                                                   params.getStandardInput(),
                                                   params.getStandardOutput(),
                                                   params.getStandardError(),
                                                   true, /* awt */
                                                   true, /* bare canvas */
                                                   params.isJavaxSoundUsed(),
                                                   params.isJDBMUsed(),
                                                   "Unix", /* OS */
                                                   params.isUnixEmulated(),
                                                   params.getFile(),
                                                   false, /* embedded */
                                                   null,  /* embedded base */
                                                   false, /* dialog */
                                                   false, /* help */
                                                   false, /* version */
                                                   false, /* disassembled */
                                                   params.getInitStackSize(),
                                                   params.getMaxStackSize(),
                                                   params.getProviders(),
                                                   params.isDispatcherCompiled()),
                            new File("."),
                            bytecode);
        bytecode.close();
        this.interpreter.execute();
    } // end empty constructor

    /**
     * {@inheritDoc}
     */
    @Override
    protected void callback(final String cb) {
        assert cb != null : "null cb";
        try {
            this.interpreter.execute(cb, Cadmium.createObject(this));
        } catch (final Throwable t) {
            showStatus(t.toString());
        } // end try/catch
    } // end method 'callback(String)'

    /**
     * {@inheritDoc}
     */
    @Override
    protected Value getCallback(final String cb) {
        return this.interpreter.getContext().getCallback(cb);
    } // end method 'getCallback(String)'

    /**
     * {@inheritDoc}
     */
    @Override
    protected Context getContext() {
        return this.interpreter.getContext();
    } // end method 'getContext()'

} // end class 'ByteCodeApplet'
